package xyz.lwm.lazycat.setting;

import org.jetbrains.annotations.NotNull;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.reflect.TypeRef;
import xyz.lwm.lazycat.utility.resource.Location;
import xyz.lwm.lazycat.utility.resource.Resource;

import java.util.Map;

/**
 * 全局 Setting 可以通过 Setting.get() 获取.
 * <pre>
 * 配置文件设置:
 * 1.在类路径下添加配置文件, application.properties 或 application.yaml 或 application.yml
 * 2.子配置文件设置, 如: application-prod.properties 或 application-dev.yaml 或 application-test.yml
 *   此时需要在步骤 1 对应的文件中添加 setting.profiles=prod 或 setting.profiles=dev 这样的配置, 用于指向子配置
 * 3.启动参数指定配置文件 --setting.location=classpath:app.properties
 *     或绝对路径 --setting.location=file:usr/local/test/app.yaml
 *     或相对路径 --setting.location=file:./config/app.yml  相对于当前工程的目录
 * </pre>
 * <pre>
 * 用法:
 * 1、获取配置系统变量和配置文件中的变量;
 *    使用 Setting.get().get("server.port").ifPresent(...)
 * 2、裁剪前缀, 获取 Setting 子对象
 *    Setting.get().forPath("server").get("port");
 * 3、获取所有变量
 *    Setting.get().asMap();
 * 4、转对象
 *    Setting.get().toBean(Student.class);
 * </pre>
 *
 * @author lwm
 */
public interface Setting {

    /**
     * 当工程打包执行时, 可以获取到工程所在目录
     * <pre>{code
     * Setting.get().get("project_path")
     * }</pre>
     */
    String PROJECT_PATH = "project_path";

    /**
     * 通过前缀获取环境变量子对象。
     *
     * <h3>for example:</h3>
     * <pre>{@code
     * student.name = hi
     * student.age = 18
     * }</pre>
     * <pre>{@code
     * Setting env = Setting.load("c/b/c.yml", Location.CLASSPATH);
     * Setting env1 = env.forPath("student");
     * env1.get("name");
     * env1.get("age");
     * }</pre>
     *
     * @param prefix key 前缀
     * @return 环境变量子对象
     */
    Setting forPath(String prefix);

    /**
     * clz 中设置了 {@link xyz.lwm.lazycat.annotation.SettingPrefix}, 可以直接转换为 clz 的对象
     *
     * @param clz 目标类
     * @return 目标对象
     * @throws SettingException 转换异常
     */
    <E> E forPath(Class<E> clz) throws SettingException;

    /**
     * 转换为 Map 对象
     */
    Map<String, String> asMap();

    /**
     * key 值不区分驼峰还是中划线
     */
    It<String> get(String key);

    /**
     * key 值不区分驼峰还是中划线
     */
    <T> It<T> get(String key, Class<T> clz);

    /**
     * 转换为指定对象.
     *
     * @param clz class
     * @return bean object
     */
    <E> E toBean(Class<E> clz) throws SettingException;

    /**
     * 转换为指定对象.
     *
     * @param typeRef type reference
     * @return bean object
     */
    <E> E toBean(TypeRef<E> typeRef) throws SettingException;

    /**
     * 获取全局 setting
     */
    @NotNull
    static Setting get() {
        return SettingLoader.get();
    }

    /**
     * 获取环境变量对象, 创建环境变量对象并加载文件
     * 只加载 .properties , .yaml , .yml  的文件;
     * 如果不同包下均有该文件, 只在加载第一个
     *
     * @param path a/b/app.yml
     * @param location  location
     *
     */
    @NotNull
    static Setting load(String path, Location location) {
        return SettingLoader.load(path, location);
    }

    /**
     * 获取环境变量对象, 创建环境变量对象并加载文件
     * 只加载 .properties , .yaml , .yml  的文件;
     *
     * @param resource resource
     * @return Setting
     */
    @NotNull
    static Setting load(Resource resource) {
        return SettingLoader.loadResource(resource);
    }

}